﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Runtime.CompilerServices;
using ForumLibrary;

namespace ForumLibrary.Data
{
    /// <summary>
    /// Summary description for DataManager
    /// </summary>
    public class DataManager
    {
        private ForumSystemDataContext _forumSystem;

        public DataManager()
        {
            _forumSystem = new ForumSystemDataContext();
        }

        [MethodImpl(MethodImplOptions.Synchronized)]
        public User getUser(string username)
        {
            User user;
            try
            {
                user = (from u in _forumSystem.Users
                        where u.Username.ToLower().Equals(username.ToLower())
                        select u).First();
            }
            catch
            {
                return null;
            }
            return user;
        }

        /// <summary>
        /// returns the friend of the user "username"
        /// this is a utility method and thus, private
        /// </summary>
        /// <param name="username">the user</param>
        /// <param name="friendsWith">the friend being searched</param>
        /// <returns>the friend that was searched</returns>
        [MethodImpl(MethodImplOptions.Synchronized)]
        private Friend getFriend(string username, string friendsWith)
        {
            Friend friend;
            try
            {
                friend = (from fr in _forumSystem.Friends
                          where fr.Username.ToLower().Equals(username.ToLower())
                          where fr.FriendsWith.ToLower().Equals(friendsWith.ToLower())
                          select fr).First();
            }
            catch
            {
                try
                {
                    friend = (from fr in _forumSystem.Friends
                              where fr.Username.ToLower().Equals(friendsWith.ToLower())
                              where fr.FriendsWith.ToLower().Equals(username.ToLower())
                              select fr).First();
                }
                catch
                {
                    return null;
                }
            }

            return friend;
        }
        
        /// <summary>
        /// searches the database searching for the
        /// username friends.
        /// this is a utility method for "getFriends" and thus, private
        /// </summary>
        /// <param name="username">the user that his friends are being searched for</param>
        /// <returns>usernames that are friends with the given usernames</returns>
        [MethodImpl(MethodImplOptions.Synchronized)]
        private List<string> getFriendsUsernames(string username)
        {
            List<string> friends1 = new List<string>();
            List<string> friends2 = new List<string>();
            try
            {
                friends1 = (from fr in _forumSystem.Friends
                            where fr.Username.ToLower().Equals(username.ToLower())
                            select fr.FriendsWith).ToList();

                friends2 = (from fr in _forumSystem.Friends
                            where fr.FriendsWith.ToLower().Equals(username.ToLower())
                            select fr.Username).ToList();
            }
            catch
            {
                friends1.AddRange(friends2);
                return friends1;
            }
            friends1.AddRange(friends2);
            return friends1;
        }

        [MethodImpl(MethodImplOptions.Synchronized)]
        public List<User> getFriends(string username)
        {
            List<User> friends = new List<User>();
            List<string> friendsUsernames = getFriendsUsernames(username);
            foreach (string user in friendsUsernames)
            {
                friends.Add(getUser(user));
            }
            return friends;
        }

        [MethodImpl(MethodImplOptions.Synchronized)]
        public string getUserPassword(string username)
        {
            User user = getUser(username);
            return user.Password;
        }

        [MethodImpl(MethodImplOptions.Synchronized)]
        private Forum getForum(string forumName)
        {
            Forum forum;
            try
            {
                forum = (from f in _forumSystem.Forums
                         where f.Topic.Equals(forumName)
                         select f).First();
            }
            catch
            {
                return null;
            }
            return forum;
        }

        [MethodImpl(MethodImplOptions.Synchronized)]
        public Thread getThread(string forumName, string threadName)
        {
            Thread thread;
            try
            {
                thread = (from t in _forumSystem.Threads
                          where t.Forum.Equals(forumName)
                          where t.Header.Equals(threadName)
                          select t).First();
            }
            catch
            {
                return null;
            }

            return thread;
        }

        [MethodImpl(MethodImplOptions.Synchronized)]
        public Post getPost(string forumName, string threadName, string postName)
        {
            Post post;
            try
            {
                post = (from p in _forumSystem.Posts
                        where p.Forum.Equals(forumName)
                        where p.Thread.Equals(threadName)
                        where p.Header.Equals(postName)
                        select p).First();
            }
            catch
            {
                return null;
            }
            return post;
        }

        [MethodImpl(MethodImplOptions.Synchronized)]
        public void addUser(string username, string pass, string email, string number, string firstname, string lastname, bool moderatorState)
        {
            User user = new User();
            user.Username = username;
            user.Password = pass;
            user.Email = email;
            user.PhoneNumber = number;
            user.FirstName = firstname;
            user.LastName = lastname;
            user.Moderator = moderatorState;
            _forumSystem.Users.InsertOnSubmit(user);
            _forumSystem.SubmitChanges();
        }

        [MethodImpl(MethodImplOptions.Synchronized)]
        public void addForum(string topic)
        {
            Forum forum = new Forum();
            forum.Topic = topic;
            _forumSystem.Forums.InsertOnSubmit(forum);
            _forumSystem.SubmitChanges();
        }

        [MethodImpl(MethodImplOptions.Synchronized)]
        public void deleteForum(string forumName)
        {
            Forum forum = getForum(forumName);
            if (forum != null)
            {
                _forumSystem.Forums.DeleteOnSubmit(forum);
                _forumSystem.SubmitChanges();
            }

        }

        [MethodImpl(MethodImplOptions.Synchronized)]
        public bool isFriend(string username, string friendToCheck)
        {
            Friend friend = getFriend(username, friendToCheck);
            if (friend != null)
                return true;

            return false;
        }

        [MethodImpl(MethodImplOptions.Synchronized)]
        public bool userExists(string username)
        {
            User user = getUser(username);
            if (user != null)
                return true;

            return false;
        }

        [MethodImpl(MethodImplOptions.Synchronized)]
        public bool moderatorExists(string username)
        {
            User user = getUser(username);
            if (user != null && user.Moderator == true)
                return true;

            return false;
        }

        [MethodImpl(MethodImplOptions.Synchronized)]
        public bool forumExists(string forumName)
        {
            Forum forum = getForum(forumName);
            if (forum != null)
                return true;

            return false;
        }

        [MethodImpl(MethodImplOptions.Synchronized)]
        public bool threadExists(string forumName, string threadName)
        {
            Thread thread = getThread(forumName, threadName);

            if (thread != null)
                return true;

            return false;
        }

        [MethodImpl(MethodImplOptions.Synchronized)]
        public bool postExists(string forumName, string threadName, string postName)
        {
            Post post = getPost(forumName, threadName, postName);

            if (post != null)
                return true;

            return false;
        }

        [MethodImpl(MethodImplOptions.Synchronized)]
        public void addThread(string username, string forumName, string header, string body)
        {
            addExclusiveThread(username, forumName, header, body, true, -1, -1, DateTime.Now);
        }

        [MethodImpl(MethodImplOptions.Synchronized)]
        private void addExclusiveThread(string username, string forumName, string header, string body, bool primaryAddition, int numOfReplies, int numOfViews, DateTime publishDate)
        {
            Thread tThread = getThread(forumName, header);

            if (tThread == null)
            {
                Thread thread = new Thread();
                thread.Creator = username;
                thread.Forum = forumName;
                thread.Header = header;
                thread.Body = body;
                thread.LastDate = DateTime.Now;

                
                if (primaryAddition)
                {
                    thread.NumOfReplies = 0;
                    thread.NumOfViews = 0;
                    thread.PublishDate = DateTime.Now;
                }
                else
                {
                    thread.NumOfReplies = numOfReplies;
                    thread.NumOfViews = numOfViews;
                    thread.PublishDate = publishDate;
                }

                _forumSystem.Threads.InsertOnSubmit(thread);
                _forumSystem.SubmitChanges();

                addPost(username, forumName, header, header, body);
            }
        }

        [MethodImpl(MethodImplOptions.Synchronized)]
        public void editThread(string username, string forumName, string threadName, string header, string body)
        {
            Thread thread = getThread(forumName, threadName);
            int numOfReplies = thread.NumOfReplies;
            int numOfViews = thread.NumOfViews;
            DateTime publishDate = thread.PublishDate;

            deletePost(username, forumName, threadName, threadName);
            _forumSystem.Threads.DeleteOnSubmit(thread);
            _forumSystem.SubmitChanges();

            addExclusiveThread(username, forumName, header, body, false, numOfReplies, numOfViews, publishDate);
        }

        [MethodImpl(MethodImplOptions.Synchronized)]
        public void deleteThread(string username, string forumName, string threadName)
        {
            Thread thread = getThread(forumName, threadName);
            deletePost(username, forumName, threadName, threadName);
            _forumSystem.Threads.DeleteOnSubmit(thread);
            _forumSystem.SubmitChanges();
        }

        [MethodImpl(MethodImplOptions.Synchronized)]
        public void addPost(string username, string forumName, string threadName, string header, string body)
        {
            addExclusivePost(username, forumName, threadName, header, body, true);
        }

        [MethodImpl(MethodImplOptions.Synchronized)]
        private void addExclusivePost(string username, string forumName, string threadName, string header, string body, bool primaryAddition)
        {
            Post tPost = getPost(forumName, threadName, header);

            if (tPost == null)
            {
                Post post = new Post();
                post.Creator = username;
                post.Forum = forumName;
                post.Header = header;
                post.Body = body;
                post.PublishDate = DateTime.Now; //published date is changed when post is edited
                post.Thread = threadName;

                Thread containedThread = getThread(forumName, threadName);
                containedThread.LastDate = post.PublishDate;
                if (primaryAddition)
                    containedThread.NumOfReplies++;

                _forumSystem.Posts.InsertOnSubmit(post);
                _forumSystem.SubmitChanges();
            }
        }

        [MethodImpl(MethodImplOptions.Synchronized)]
        public void editPost(string username, string forumName, string threadName, string postName, string newHeader, string newBody)
        {
            Post post = getPost(forumName, threadName, postName);
            _forumSystem.Posts.DeleteOnSubmit(post);
            _forumSystem.SubmitChanges();

            addExclusivePost(username, forumName, threadName, newHeader, newBody, false);

            Thread containedThread = getThread(forumName, threadName);
            containedThread.LastDate = DateTime.Now;
            _forumSystem.SubmitChanges();
        }

        [MethodImpl(MethodImplOptions.Synchronized)]
        public void loadUsers(ref List<ForumUser> clients)
        {
            List<User> users = _forumSystem.Users.ToList();
            foreach (User user in users)
            {
                ForumUser client = new ForumUser(user.Username, null, false);
                clients.Add(client);
            }
        }

        [MethodImpl(MethodImplOptions.Synchronized)]
        public List<Post> getPosts(string forumName, string threadName)
        {
            Thread thread = getThread(forumName, threadName);
            thread.NumOfViews++;
            
            List<Post> posts = new List<Post>();
            try
            {
                posts = (from p in _forumSystem.Posts
                         where p.Forum.Equals(forumName)
                         where p.Thread.Equals(threadName)
                         orderby p.PublishDate
                         select p).ToList();
            }
            catch
            {
                return posts;
            }
            return posts;
        }

        [MethodImpl(MethodImplOptions.Synchronized)]
        public List<Thread> getThreads(string forumName)
        {
            List<Thread> threads = new List<Thread>();
            try
            {
                threads = (from t in _forumSystem.Threads
                           where t.Forum.Equals(forumName)
                           select t).ToList();
            }
            catch
            {
                return threads;
            }
            return threads;
        }

        [MethodImpl(MethodImplOptions.Synchronized)]
        public List<Forum> getForums()
        {
            return _forumSystem.Forums.ToList();
        }

        [MethodImpl(MethodImplOptions.Synchronized)]
        public IQueryable<resultobj> getForumsData()
        {
            IQueryable<resultobj> frm = null;
            try
            {
                frm = (from t in _forumSystem.Threads
                       group t by t.Forum into f
                       select new resultobj { _Topic = f.Key, _TotalThreads = f.Count(), _recentDate = String.Format("{0:dd/MM/yyyy HH:mm tt}",f.Max(t => t.LastDate)) });
            }
            catch
            {
                return frm;
            }
            return frm;
        }

        public class resultobj
        {
            public string _Topic;
            public int _TotalThreads;
            public string _recentDate;
            public int TotalThreads { get { return _TotalThreads; } }
            public string Topic { get { return _Topic; } }
            public string RecentDate { get { return _recentDate; } }
        }

        [MethodImpl(MethodImplOptions.Synchronized)]
        public List<User> getUsers()
        {
            return _forumSystem.Users.ToList();
        }

        [MethodImpl(MethodImplOptions.Synchronized)]
        public void deletePost(string username, string forumName, string threadName, string postName)
        {
            Thread thread = getThread(forumName, threadName);
            thread.NumOfReplies--;
            Post post = getPost(forumName, threadName, postName);
            _forumSystem.Posts.DeleteOnSubmit(post);
            _forumSystem.SubmitChanges();
        }

        [MethodImpl(MethodImplOptions.Synchronized)]
        public string getThreadOwner(string forumName, string threadName)
        {
            Thread thread = getThread(forumName, threadName);

            return thread.Creator;
        }

        [MethodImpl(MethodImplOptions.Synchronized)]
        public string getPostOwner(string forumName, string threadName, string postName)
        {
            Post post = getPost(forumName, threadName, postName);

            return post.Creator;
        }

        [MethodImpl(MethodImplOptions.Synchronized)]
        public void addFriend(string friendAdding, string friendAdded)
        {
            Friend friend = new Friend();
            friend.Username = friendAdding;
            friend.FriendsWith = friendAdded;
            _forumSystem.Friends.InsertOnSubmit(friend);
            _forumSystem.SubmitChanges();
        }

        [MethodImpl(MethodImplOptions.Synchronized)]
        public void removeFriend(string friendRemoving, string friendRemoved)
        {
            Friend userRemoving = getFriend(friendRemoving, friendRemoved);
            if (userRemoving != null)
            {
                _forumSystem.Friends.DeleteOnSubmit(userRemoving);
                _forumSystem.SubmitChanges();
            }
        }
        //
        [MethodImpl(MethodImplOptions.Synchronized)]
        public List<User> searchForNewFriend(string username, string searchedUser)
        {
            List<User> potentialFriends;
            List<User> friends = getFriends(username);
            User currentUser = getUser(username);
            if (searchedUser == null)
            {
                potentialFriends = _forumSystem.Users.ToList();
            }
            else
            {
                potentialFriends = new List<User>();
                potentialFriends = (from fr in _forumSystem.Users
                                    where fr.Username.ToLower().Contains(searchedUser.ToLower())
                                    select fr).ToList();
            }

            potentialFriends.RemoveAll(user => user.Username.ToLower().Equals(username.ToLower()));
            foreach (User friend in friends)
            {
                potentialFriends.Remove(friend);
            }

            return potentialFriends;
        }
    }
}